/*
Kiwi Editor is a framework for building WYSIWYG web editors.
Copyright (C) 2011  Roosevelt de Lima Sardinha

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

Contact the author at roosevelt1@gmail.com (Roosevelt) 
*/

/**
 * Manages components of the workspace like edition areas, toolboxes, property boxes and instruction boxes.
 * @author Roosevelt de Lima Sardinha
 * @constructor
 */
function workspace(){
	/**
	 * The id of the workspace.
	 */
	this.id = null;
	
	/**
	 * The profile that is being used by the toolbox.
	 * @type {Int}
	 */
	this.activeProfile = null;
	
	/**
	 * Array that keeps all edition areas in this workspace.
	 * @type {Array}
	 */
	this.editionAreas = new Array;

	/**
	 * Array that keeps all edition areas in this workspace.
	 * @type {Array}
	 */
	this.propertyBoxes = new Array;
	
	/**
	 * Array that keeps all instruction boxes in this workspace.
	 * @type {Array}
	 */
	this.instructionBoxes = new Array;
	
	/**
	 * Array that keeps all edition areas in this workspace.
	 * @type {Array}
	 */
	this.toolBoxes = new Array;
	
	/**
	 * Array that keeps all profiles in this workspace.
	 * @type {Array}
	 */
	this.profiles = new Array;
	
	/**
	 * Takes the keyboard object that is being used for taking care of key pressing in the workspace.
	 * @type {Object}
	 */
	this.keyboard = new keyboard();
	
	/**
	 * <b>Event</b><br/>
	 * Defines what action is going to be taken before creating an editionArea.
	 */
	this.onBeforeCreateEditionArea = function(){};
	/**
	 * <b>Event</b><br/>
	 * Defines what action is going to be taken before creating a propertyBox.
	 */
	this.onBeforeCreateToolBox = function(){};
	
	/**
	 * <b>Event</b><br/>
	 * Defines what action is going to be taken before creating a propertyBox.
	 */
	this.onBeforeCreatePropertyBox = function(){};
	
	/**
	 * <b>Event</b><br/>
	 * Defines what action is going to be taken before creating an InstructionBox.
	 */
	this.onBeforeCreateInstructionBox = function(){};
	
	/**
	 * <b>Event</b><br/>
	 * Defines what action is going to be taken after creating an edition area.
	 */
	this.onAfterCreateEditionArea = function(editionArea){};
	
	/**
	 * <b>Event</b><br/>
	 * Defines what action is going to be taken after creating a toolbox.
	 */
	this.onAfterCreateToolBox = function(toolBox){};
	
	/**
	 * <b>Event</b><br/>
	 * Defines what action is going to be taken after creating a property box.
	 */
	this.onAfterCreatePropertyBox = function(propertyBox){};
	
	/**
	 * <b>Event</b><br/>
	 * Defines what action is going to be taken after creating an instruction box.
	 */
	this.onAfterCreateInstructionBox = function(instructionBox){};

	/**
	 * Creates an edition area.
	 * @param {String} id The unique id of this edition area
	 * @param {Object} properties The properties to change attributes like left, top, width, height, parent, and others
	 * @return The edition area created with the properties set.
	 * @type {Object} 
	 */
    this.createEditionArea = function(id, properties){
        this.onBeforeCreateEditionArea();
		// Creates an Edition Area that can receive EOs.
        var ea = new editionArea(id, {
			left: properties.left,
			top: properties.top, 
			width: properties.width, 
			height: properties.height, 
			parent: properties.parent,
			toolBox: properties.toolBox, 
			propertyBox: properties.propertyBox, 
			instructionBox: properties.instructionBox,
			workspace: this
		});
        this.editionAreas.push(ea);
		
		// Calling Event
		this.onAfterCreateEditionArea(ea);	
        return ea;
    }
	
	 /**
	 * Creates a tool box.
	 * @param {String} id The unique id of this tool box
	 * @param {Object} properties The properties to change attributes like left, top, width, height, parent, and others
	 * @return The toolbox created with the properties set.
	 * @type {Object} 
	 */   
    this.createToolBox = function(id, properties){
        this.onBeforeCreateToolBox();
		// Create a Toolbox where you can add some tools.
        var tb = new toolBox(id, {
			left: properties.left,
		    top: properties.top, 
			width: properties.width,
			height: properties.height,
			parent: properties.parent,
			workspace: this
			});
        this.toolBoxes.push(tb);
		
		// Calling Event
		this.onAfterCreateToolBox(tb);
        return tb;
    }
    
	 /**
	 * Creates a property box.
	 * @param {String} id The unique id of this property box
	 * @param {Object} properties The properties to change attributes like left, top, width, height, parent, and others
	 * @return The property box created with the properties set.
	 * @type {Object} 
	 */ 	
    this.createPropertyBox = function(id, properties){
        this.onBeforeCreatePropertyBox();
		
		// Create a box with the properties of the selected objects.
        var pb = new propertyBox(id, {
			left: properties.left,
		    top: properties.top, 
			width: properties.width,
			height: properties.height,
			parent: properties.parent,
			workspace: this
			});
        this.propertyBoxes.push(pb);
        
		// Calling Event
		this.onAfterCreatePropertyBox(pb);
		return pb;
    }
    
	
	/**
	 * Creates an instruction box.
	 * @param {String} id The unique id of this instruction box
	 * @param {Object} properties The properties to change attributes like left, top, width, height, parent, and others
	 * @return The instruction box created with the properties set.
	 * @type {Object} 
	 */ 
    this.createInstructionBox = function(id, properties){
        this.onBeforeCreateInstructionBox();
		// Creates a box with instruction for helping users using kiwi.
        var ib = new instructionBox(id, {
			left: properties.left,
		    top: properties.top, 
			width: properties.width,
			height: properties.height,
			parent: properties.parent,
			workspace: this
			});
        this.instructionBoxes.push(ib);
		
		// Calling Event
		this.onAfterCreateInstructionBox(ib);
        return ib;
    }
	
	function search(elementId, array){
        var index = -1;
        for (var i = 0; i < array.length; i++) {
            if (array[i].id == elementId) {
                index = i;
                break;
            }
        }
        
        if (index != -1) {
            return {obj: array[index], index: index};
        }
        else {
            return index;
        }
	}
	
	/**
	 * Returns an edition area given its id 
	 * @param {String} id The id of the edition area to be returned
	 * @return The edition area that has the given id.
	 * @type {Object}
	 */	
	this.getEditionAreaById =  function(id){
		return search(id, this.editionAreas).obj;		
	}

	/**
	 * Returns a property box given its id 
	 * @param {String} id The id of the property box to be returned
	 * @return The property box that has the given id
	 * @type {Object}
	 */		
	this.getPropertyBoxById = function(id){
		return search(id, this.propertyBoxes).obj;
	}

	/**
	 * Returns an instruction box given its id 
	 * @param {String} id The id of the instruction box to be returned
	 * @return The instrcution box that has the given id
	 * @type {Object}
	 */			
	this.getInstructionBoxById = function(id){
		return search(id, this.instructionBoxes).obj;
	}
	
	
	/**
	 * Returns a tool box given its id 
	 * @param {String} id The id of the tool box to be returned
	 * @return The tool box that has the given id
	 * @type {Object}
	 */		
	this.getToolBoxById = function(id){
		return search(id, this.toolBoxes).obj;
	}
	
	
	function remove(id, array ){
		var element = search(id, array);
		element.obj.remove();
		array.splice(element.index, 1);
		delete element;
	}
	
	/**
	 * Removes an edition area given its id 
	 * @param {String} id  The id of the edition area 
	 */		
	this.removeEditionArea = function(id){
		remove(id, this.editionAreas);
	}

	/**
	 * Removes a property box given its id 
	 * @param {String} id  The id of the property box 
	 */		
	this.removePropertyBox = function(id){
		remove(id, this.propertyBoxes);
	}
	
	/**
	 * Removes an instruction box given its id 
	 * @param {String} id  The id of the instruction box 
	 */			
	this.removeInstructionBox = function(id){
		remove(id, this.instructionBoxes);
	}
	
	/**
	 * Removes a toolBox given its id 
	 * @param {String} id  The id of the toolBox 
	 */		
	this.removeToolBox = function(id){
		remove(id, this.toolBoxes);
	}
	
	function retrieveByIndex(index, array){
		if (array.length >= index){
			return null;
		}else{
			return array[index];
		}
	}
	
	/**
	 * Returns an edition area by its index
	 * @param {Int} index The position of the edition area in the editionAreas array. It depends on the order of creation.
	 * @return The edition area corresponding to the given index
	 * @type {object}
	 */	
	this.getEditionAreaByIndex = function(index){
		return retrieveByIndex(index,this.editionAreas); 
	}

	/**
	 * Returns a property box by its index
	 * @param {Int} index The position of the property box in the propertyBoxes array. It depends on the order of creation.
	 * @return The property box corresponding to the given index
	 * @type {Object}
	 */	
	this.getPropertyBoxByIndex = function(index){
		return retrieveByIndex(index,this.propertyBoxes);
	}

	/**
	 * Returns an instruction box by its index
	 * @param {Int} index The position of the instruction box in the instructionBoxes array. It depends on the order of creation.
	 * @return The instruction box corresponding to the given index
	 * @type {Object}
	 */		
	this.getInstructionBoxByIndex = function(index){
		return retrieveByIndex(index,this.instructionBoxes);
	}

	/**
	 * Returns a toolbox by its index
	 * @param {Int} index The position of the toolbox in the toolBoxes array. It depends on the order of creation.
	 * @return The toolbox corresponding to the given index
	 * @type {Object}
	 */		
	this.getToolBoxByIndex = function(index){
		return retrieveByIndex(index,this.toolBoxes);
	}
	
	/**
	 * Sets a profile to the toolbox.
	 * @param {Int} profile_id The id of the profile to be loaded
	 * @param {String} toolbox_id The toolbox where the profile is going to be loaded
	 * @param {Object} properties The properties the tools are going to take, like the propertyBox to show its properties,  
	 * instruction box to show help, hint, and others.
	 */
	this.setActiveProfile = function(profile_id, toolbox_id, properties){
		var toolbox = this.getToolBoxById(toolbox_id);
		toolbox.removeAllTools();
		toolbox.loadProfile(profile_id, properties);
		this.activeProfile = profile_id;
	}
	
	/**
	 * Sets a value to a workspace property.
	 * @param {String} property The property that is going to be changed.
	 * @param {Any} value The value to set to the property.
	 */
	this.setProperty = function(property, value){
		switch (property){			
			case 'activeProfile':
				this.activeProfile = value;
				break;
				
			default:
				break;
		}	
	}
	
	/**
	 * Saves a workspace to the database.
	 */
	this.save = function(){
		var exists = null;
		var id;
		var workspaceJSON = {};
		
		if (this.activeProfile) {
			workspaceJSON.current_profile = this.activeProfile;
		}else{
			var pm = new profileManager();
			profile = pm.createProfile();
			profile.name = "default";
			profile.description = "default";
			profile.types = ws.toolBoxes[0].toolList;
			profile.save();
			this.activeProfile = profile.id; 
			workspaceJSON.current_profile = profile.id;
		}
		workspaceJSON.profiles = this.profiles;
		
		// Building edition areas json
		var editionAreasArray = [];
		var editionAreasJSON = {};
		var editionArea = null;
		for (var i =0; i<this.editionAreas.length; i++){
			editionArea = this.editionAreas[i];
			
			editionAreasJSON.id = editionArea.id;
			editionAreasJSON.title = editionArea.title;
			editionAreasJSON.css_path = editionArea.cssPath;
			editionAreasJSON.top = editionArea.top;
			editionAreasJSON.left = editionArea.left;
			editionAreasJSON.height = editionArea.height;
			editionAreasJSON.width = editionArea.width;
			editionAreasJSON.parent = editionArea.parentId;
			
			editionAreasArray.push(editionAreasJSON);			
		}
		
		workspaceJSON.edition_areas = editionAreasArray; 
		
		// Building instruction_boxes json
		
		var instructionBoxesArray = [];
		var instructionBoxesJSON = {};
		var instructionBox = null;
		for (var i =0; i<this.instructionBoxes.length; i++){
			instructionBox = this.instructionBoxes[i];
			
			instructionBoxesJSON.id = instructionBox.id;
			instructionBoxesJSON.title = instructionBox.title;
			instructionBoxesJSON.css_path = instructionBox.cssPath;
			instructionBoxesJSON.top = instructionBox.top;
			instructionBoxesJSON.left = instructionBox.left;
			instructionBoxesJSON.height = instructionBox.height;
			instructionBoxesJSON.width = instructionBox.width;
			instructionBoxesJSON.parent = instructionBox.parentId;
			
			instructionBoxesArray.push(instructionBoxesJSON);			
		}
		
		workspaceJSON.instruction_boxes = instructionBoxesArray; 
		
		// Building property_boxes json
		
		var propertyBoxesArray = [];
		var propertyBoxesJSON = {};
		var propertyBox = null;
		for (var i =0; i<this.propertyBoxes.length; i++){
			propertyBox = this.propertyBoxes[i];
			
			propertyBoxesJSON.id = propertyBox.id;
			propertyBoxesJSON.title = propertyBox.title;
			propertyBoxesJSON.css_path = propertyBox.cssPath;
			propertyBoxesJSON.top = propertyBox.top;
			propertyBoxesJSON.left = propertyBox.left;
			propertyBoxesJSON.height = propertyBox.height;
			propertyBoxesJSON.width = propertyBox.width;
			propertyBoxesJSON.parent = propertyBox.parentId;
			
			propertyBoxesArray.push(propertyBoxesJSON);			
		}
		
		workspaceJSON.property_boxes = propertyBoxesArray;
		

		// Building tool_boxes json
		
		var toolBoxesArray = [];
		var toolBoxesJSON = {};
		var toolBox = null;
		for (var i =0; i< this.toolBoxes.length; i++){
			toolBox = this.toolBoxes[i];
			
			toolBoxesJSON.id = toolBox.id;
			toolBoxesJSON.title = toolBox.title;
			toolBoxesJSON.css_path = toolBox.cssPath;
			toolBoxesJSON.top = toolBox.top;
			toolBoxesJSON.left = toolBox.left;
			toolBoxesJSON.height = toolBox.height;
			toolBoxesJSON.width = toolBox.width;
			toolBoxesJSON.parent = toolBox.parentId;
			
			toolBoxesArray.push(toolBoxesJSON);			
		}
		
		workspaceJSON.tool_boxes = toolBoxesArray;
		
		var strJSON = JSON.stringify(workspaceJSON);
		
		var params = [];
		params['json'] = strJSON;
		
		var myRequest = new request();	
		if (this.id != null) {
			myRequest.sendSincRequest('GET', null, "workspaces/" + this.id + "/exist", function(response){
				exists = response;
			});
		}
		
		// Check if workspace already exists
		if ((exists != null) && JSON.parse(exists)){
			params['_method'] = 'PUT';
	        myRequest.sendSincRequest('POST', params, "workspaces/" + this.id, function(response){
			});
		}else{
	        myRequest.sendSincRequest('POST', params, "workspaces", function(response){
				id = response;
			});
			this.id = JSON.parse(id);		
		}
	}
	
	/**
	 * Generates a XML corresponding to the workspace in the database. Save before using this method if you need an up-to-date XML.
	 * @return The XML corresponding to this workspace.
	 * @type {String}
	 */
	this.generateXML = function(){
		var workspaceStr;
		var myRequest = new request();
		myRequest.sendSincRequest('GET', null, "workspaces/" + this.id  + ".xml", function(response){
		    workspaceStr = response;
		});
		return workspaceStr;   
	}
	
	/**
	 * Changes the CSS stylesheet associated with a box.
	 * @param {Object} box The box to change the CSS stylesheet.
	 * @param {String} path The path of the new CSS stylesheet file. It refers to the APP_HOME path constant in the kiwi.js file.
	 */		
	this.setCSS = function(box, path){
		// Sets a css file to one of the box in the workspace.
		var documentHead = document.getElementsByTagName('head')[0];	
		
		// Remove old link tag if it alread exists
		var linkTagExist = false;
		link_tags = documentHead.getElementsByTagName('link');
		if (link_tags){
			for (var j = 0; j < link_tags.length; j++) {
				if (link_tags[j].href == (location.protocol + "//" + location.hostname + ":" + location.port + APP_HOME + box.cssPath)) {
					link_tags[j].href = APP_HOME + path;
					box.cssPath = path; 
					linkTagExist = true;	
					break;
				}
			}
		}
		
		if (!linkTagExist) {
			// Add new link tag	
			var link = document.createElement('link');
			link.rel = "stylesheet";
			link.type = "text/css";
			link.href = APP_HOME + path;
			documentHead.appendChild(link);
		}
	}
}

